using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Collections;
using System.Data.Common;
using ResourceBlender_Express.Properties;
using Microsoft.Win32;
using System.IO;

namespace ResourceBlender_Express {
    /// <summary>
    /// A window for generating formatted resource files.
    /// </summary>
    public partial class WinGenerate : Window {
        public static RoutedUICommand GenerateResources = new RoutedUICommand("GenerateResources", "GenerateResources", typeof(WinGenerate), new InputGestureCollection(new InputGesture[] { new KeyGesture(Key.G, ModifierKeys.Control, "Generate Resources") }));
        public static RoutedUICommand UpdateFilename = new RoutedUICommand("UpdateFilename", "UpdateFilename", typeof(WinGenerate));

        /// <summary>
        /// Initializes a new <see cref="WinGenerate"/>.
        /// </summary>
        public WinGenerate() {
            InitializeComponent();
        }

        /// <summary>
        /// Loads the list of available formats.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void Window_Loaded(object sender, RoutedEventArgs e) {
            BackgroundWorker worker = new BackgroundWorker();
            WinProgress progress = new WinProgress("Loading formats...", true);
            worker.DoWork += (s, dwe) => {
                Database db = null;
                try {
                    db = new Database();
                    db.OpenConnection();
                    using(Database.Command cmd = db.CreateCommand()) {
                        SortedDictionary<string, string> types = new SortedDictionary<string, string>();
                        foreach(KeyValuePair<string,string> template in EnumBinder.EnumHelper.ToList(typeof(Constants.ResourceType))) {
                            types.Add(template.Value, template.Key);
                        }
                        cmd.CommandText = "SELECT id, name FROM " + db.TablePrefix + "templates";
                        DbDataReader reader = cmd.ExecuteReader();
                        while(reader.Read()) {
                            types.Add(reader["name"].ToString(), reader["id"].ToString());
                        }
                        dwe.Result = types;
                    }
                } catch(Exception ex) {
                    dwe.Result = "Error #453: Could not load resource formats - " + ex.Message;
                } finally {
                    try {
                        db.CloseConnection();
                    } catch { }
                }
            };
            worker.RunWorkerCompleted += (s, rwe) => {
                progress.Close();
                if(rwe.Result is string) {
                    MessageBox.Show(this, rwe.Result.ToString(), "Database Error", MessageBoxButton.OK, MessageBoxImage.Error);
                } else {
                    lstFormat.DataContext = rwe.Result;
                    lstFormat.SelectedIndex = 0;
                }
            };
            progress.Show();
            worker.RunWorkerAsync();
        }

        /// <summary>
        /// Refreshes the applications.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void RefreshApplications(object sender, RoutedEventArgs e) {
            applicationSelector.RefreshApplications();
        }

        /// <summary>
        /// Shows a dialog to browse for an output file.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void BrowseForFile(object sender, RoutedEventArgs e) {
            SaveFileDialog dlg = new SaveFileDialog();
            dlg.DefaultExt = ".zip";
            dlg.AddExtension = true;
            dlg.CheckPathExists = true;
            dlg.FileName = txtOutputPath.Text;
            dlg.Filter = "Zip files (.zip)|*.zip";
            dlg.OverwritePrompt = true;
            dlg.Title = "Output File";
            if(dlg.ShowDialog() == true) {
                txtOutputPath.Text = dlg.FileName;
            }
        }

        /// <summary>
        /// Closes the window.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void CloseWindow(object sender, RoutedEventArgs e) {
            Close();
        }

        /// <summary>
        /// Validates user input then generates resources.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void GenerateResourcesHandler(object sender, ExecutedRoutedEventArgs e) {
            txtThreshold.GetBindingExpression(TextBox.TextProperty).UpdateSource();
            txtOutputPath.GetBindingExpression(TextBox.TextProperty).UpdateSource();
            if(!applicationSelector.ValidateSelection(true)) {
                return;
            }
            if(string.IsNullOrEmpty(txtThreshold.Text)) {
                MessageBox.Show(this, "The completion threshold must be entered", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                txtThreshold.Focus();
                return;
            }
            string outputFileName = txtOutputPath.Text.Trim();
            if(string.IsNullOrEmpty(outputFileName)) {
                MessageBox.Show(this, "The output filename must be entered", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                txtOutputPath.Focus();
                return;
            }
            FileInfo outputFile = null;
            try {
                outputFile = new FileInfo(outputFileName);
            } catch {
                MessageBox.Show(this, "Invalid filename", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                txtOutputPath.Focus();
                return;
            }
            try {
                DirectoryInfo directory = new DirectoryInfo(outputFile.DirectoryName);
                if(!directory.Exists) {
                    MessageBox.Show(this, "The output directory does not exist", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                    txtOutputPath.Focus();
                    return;
                }
            } catch {
                MessageBox.Show(this, "The output directory does not exist", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                txtOutputPath.Focus();
                return;
            }
            if(outputFile.Exists) {
                if(MessageBox.Show(this, "The output file already exists. Overwrite it?", "Confirm Overwrite", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No) == MessageBoxResult.Yes) {
                    outputFile.Delete();
                } else {
                    txtOutputPath.Focus();
                    return;
                }
            }
            Settings.Default.LastGenerationPath = outputFile.DirectoryName;
            Settings.Default.Save();
            RBApplication selectedApplication = applicationSelector.SelectedApplication;
            ObservableSortedDictionary<string, string> selectedBundles = applicationSelector.SelectedBundles,
                                                      selectedLanguages = applicationSelector.SelectedLanguages;
            string[] bundles = selectedBundles.Keys.ToArray(),
                     languages = selectedLanguages.Keys.ToArray();
            int threshold = int.Parse(txtThreshold.Text);
            bool includeSummary = chkIncludeSummary.IsChecked == true;
            int formatIndex = lstFormat.SelectedIndex;
            string format = lstFormat.SelectedValue as string;

            BackgroundWorker worker = new BackgroundWorker();
            WinProgress progress = new WinProgress("Generating Resources...", true);
            worker.DoWork += (s, dwe) => {
                try {
                    if(formatIndex < Enum.GetValues(typeof(Constants.ResourceType)).Length) {
                        Constants.ResourceType type = (Constants.ResourceType)Enum.Parse(typeof(Constants.ResourceType), format);
                        switch(type) {
                            case Constants.ResourceType.Firefox:
                                bool hasXul = false,
                                     hasJs = false;
                                foreach(KeyValuePair<string, string> bundle in selectedBundles) {
                                    if(bundle.Value.ToLowerInvariant() == "xul") {
                                        hasXul = true;
                                    } else if(bundle.Value.ToLowerInvariant() == "javascript") {
                                        hasJs = true;
                                    }
                                   
                                }
                                if(hasXul && hasJs) {
                                    ResourceGenerator.GenerateFirefox(outputFile, selectedApplication.ApplicationName, selectedApplication.ID, threshold, bundles, languages, selectedApplication.DefaultLanguage, includeSummary);
                                } else {
                                    dwe.Result = "Bundles named JavaScript and XUL must be present to output to this format. The selected application lacks one or both of these bundles.";
                                }
                                break;
                            case Constants.ResourceType.Java:
                                ResourceGenerator.GenerateJava(outputFile, selectedApplication.ApplicationName, selectedApplication.ID, threshold, bundles, languages, selectedApplication.DefaultLanguage, includeSummary);
                                break;
                            case Constants.ResourceType.DotNet:
                                ResourceGenerator.GenerateDotNet(outputFile, selectedApplication.ApplicationName, selectedApplication.ID, threshold, bundles, languages, selectedApplication.DefaultLanguage, includeSummary, false);
                                break;
                            case Constants.ResourceType.DotNetCF:
                                ResourceGenerator.GenerateDotNet(outputFile, selectedApplication.ApplicationName, selectedApplication.ID, threshold, bundles, languages, selectedApplication.DefaultLanguage, includeSummary, true);
                                break;
                            case Constants.ResourceType.PO:
                                ResourceGenerator.GeneratePO(outputFile, selectedApplication.ApplicationName, selectedApplication.ID, threshold, bundles, languages, selectedApplication.DefaultLanguage, includeSummary);
                                break;
                        }
                    } else { // not a preset, must be a custom format
                        Database db = null;
                        try {
                            db = new Database();
                            db.OpenConnection();
                            using(Database.Command cmd = db.CreateCommand()) {
                                cmd.CommandText = "SELECT filename, filestart, line, fileend FROM " + db.TablePrefix + "templates WHERE id = @templateId";
                                cmd.AddParameterWithValue("@templateId", format);
                                DbDataReader reader = cmd.ExecuteReader();
                                if(!reader.Read()) {
                                    throw new ArgumentException("Template not found:" + selectedApplication.ID);
                                }
                                string templateFileName,
                                       templateFileStart,
                                       templateFileEnd,
                                       templateElement;
                                try {
                                    templateFileName = reader["filename"].ToString();
                                    templateFileStart = reader["filestart"].ToString();
                                    templateFileEnd = reader["fileend"].ToString();
                                    templateElement = reader["line"].ToString();
                                } catch {
                                    db.CloseConnection();
                                    throw new ArgumentException("Could not load template data");
                                }
                                Console.WriteLine(templateElement);
                                reader.Close();
                                KeyValuePair<string, Dictionary<string, Dictionary<string, Dictionary<string, string>>>> rawResources = ResourceGenerator.GenerateBundles(selectedApplication.ApplicationName, selectedApplication.ID, threshold, bundles, languages, selectedApplication.DefaultLanguage, includeSummary);
                                ResourceGenerator.GenerateCustom(outputFile, selectedApplication.ApplicationName, rawResources, templateFileName, templateFileStart, templateFileEnd, templateElement);
                            }
                        } catch(Exception ex) {
                            dwe.Result = ex.Message;
                        } finally {
                            if(db != null) {
                                try {
                                    db.CloseConnection();
                                } catch { }
                            }
                        }
                    }
                } catch(Exception ex) {
                    dwe.Result = ex.Message;
                }
            };
            worker.RunWorkerCompleted += (s, rwe) => {
                progress.Close();
                if(rwe.Result is string) {
                    MessageBox.Show(this, rwe.Result.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                } else {
                    
                }
            };
            progress.Show();
            worker.RunWorkerAsync(new object[] {
                   applicationSelector.SelectedApplication,
                   applicationSelector.SelectedBundles,
                   applicationSelector.SelectedLanguages,
                   int.Parse(txtThreshold.Text),
                   chkIncludeSummary.IsChecked == true,
                   lstFormat.SelectedValue
            });
        }

        /// <summary>
        /// Fires the UpdateFilename command
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void TriggerUpdateFilename(object sender, RoutedEventArgs e) {
            WinGenerate.UpdateFilename.Execute(null, this);
        }

        /// <summary>
        /// Updates the filename to reflect the application selected.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void UpdateFilenameHandler(object sender, ExecutedRoutedEventArgs e) {
            if(applicationSelector.SelectedApplication != null) {
                string fileName = string.Format("{0} - {1}.zip", applicationSelector.SelectedApplication.DisplayName, DateTime.Now.ToString("yyyy-MM-dd"));
                string path = Settings.Default.LastGenerationPath.Length > 0 ? Settings.Default.LastGenerationPath : Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
                txtOutputPath.Text = System.IO.Path.Combine(path, fileName);
            }
        }
    }
}
